//
//  SimplePSInterpreter.m
//  T1Wrap
//
//  Created by Massimiliano Gubinelli on 10/02/2005.
//  Copyright 2005 Massimiliano Gubinelli. All rights reserved.
//

// Based on a Perl script by Jonathan Kew (February 2005)

#import "SimplePSInterpreter.h"

static NSString *SimplePSInterpreterException = @"SimplePSInterpreterException";

@interface NSMutableArray (Stack)
- pop;
@end

@implementation NSMutableArray (Stack)
- pop
{ 
  id value = [[[self lastObject] retain] autorelease];
  [self removeLastObject];
  return value;
}
@end

@interface SimplePSInterpreter (Private)
- (void) execProc:(NSArray *)proc;
- (void) execObj:(id)obj;
- (void) execName:(NSString *)identifier;

// PS operators
- (void) opString;
- (void) opReadstring;
- (void) opDef;
- (void) opNot;
- (void) opDict;
- (void) opBegin;
- (void) opEnd;
- (void) opPop;
- (void) opDup;
- (void) opNop;
- (void) opGet;
- (void) opPut;
- (void) opGetinterval;
- (void) opPutinterval;
- (void) opArray;
- (void) opMark;
- (void) opCleartomark;
- (void) opExch;
- (void) opIndex;
- (void) opFor;
- (void) opKnown;
- (void) opIf;
- (void) opIfelse;
- (void) opCurrentdict;
- (void) opCurrentfile;
- (void) opClosefile;
- (void) opDefinefont;
- (void) opEexec;
- (void) opExec;
- (void) opNe;
- (void) opEq;
- (void) initializeInterpreter;
@end // interface

@implementation SimplePSInterpreter

static NSString *StackMarkObject = @" MARK ";
static NSString *NullPSObject = @"[NIL]";

- (void) opString
{
  int n = 0;
  id obj = [stack pop];
  if (![obj isKindOfClass:[NSNumber class]])
	[NSException raise:SimplePSInterpreterException format:@"string parameter is not a number"];
  else n = [obj intValue];
#if 0
  NSMutableString *emptyString = [NSMutableString stringWithCapacity:[(NSNumber*)obj intValue]];
  while (n--) [emptyString appendString:@" "];
  [stack addObject:emptyString];	
#else
//  char *ss = alloca(n+1);
  char *ss = alloca(n);
//  ss[n] = 0x0;
  int i = n;
  while (i) ss[--i] = ' ';
//  [stack addObject:[NSString stringWithCString:ss length:n]];
  [stack addObject:[[NSString alloc] initWithBytes:ss length:n encoding:NSMacOSRomanStringEncoding]];
#endif
  
}

- (void) opReadstring
{
  id stringobj = [stack pop];
  id fileobj = [stack pop];
  if (![fileobj isKindOfClass:[NSString class]] || ![stringobj isKindOfClass:[NSString class]] )
	[NSException raise:SimplePSInterpreterException format:@"wrong arguments in readstring"];
  if (![(NSString*)fileobj isEqualToString:@" <FILE> "])
	[NSException raise:SimplePSInterpreterException format:@"cannot read string from file %@",fileobj];
  int len = [(NSString*)stringobj length];

  NSString *dataString = [inputStream substringWithRange:NSMakeRange([sc scanLocation]+1,len)];
  [sc setScanLocation:[sc scanLocation]+1+len]; // skip whitespace & data string
  [stack addObject:dataString];
  [stack addObject:[NSNumber numberWithInt:1]];
}

- (void) opDef
{
  id obj = [stack pop];
  id nameobj = [stack pop];
  if (![nameobj isKindOfClass:[NSString class]]) 
	[NSException raise:SimplePSInterpreterException format:@"defining non name"];
  else {
	NSString *name = nameobj;
	if ([name characterAtIndex:0] != '/') NSLog(@"Warning: defining non literal name");
	[(NSMutableDictionary*)[dicts lastObject] setObject:obj forKey:name];
	[(NSMutableArray*)[(NSDictionary*)[dicts lastObject] objectForKey:@"[KEY ORDER]"] addObject:name];
  }
}

- (void) opNot
{
  id myBool = [stack pop];
  if ([myBool isKindOfClass:[NSNumber class]]) {
	[stack addObject:[NSNumber numberWithInt:([(NSNumber*)myBool intValue]? 0 : 1)]];
  }
  else [NSException raise:SimplePSInterpreterException format:@"cannot apply <not> to non-boolean object"];
}

- (void) opNe
{
  id a = [stack pop];
  id b = [stack pop];
  [stack addObject:[NSNumber numberWithInt:([a isEqualTo:b] ? 0 : 1 )]];
}


- (void) opEq
{
  id a = [stack pop];
  id b = [stack pop];
  [stack addObject:[NSNumber numberWithInt:([a isEqualTo:b] ? 1 : 0 )]];
}

- (void) execProc:(NSArray *)procwrapper
{
  NSArray *proc = [procwrapper objectAtIndex:1];
  NSEnumerator *en = [proc objectEnumerator];
  id obj;
  while ((obj = [en nextObject])) {
	[self execObj:obj];
  }
}

- (void) execObj:(id)obj
{
  if ([obj isKindOfClass:[NSInvocation class]])
  {
	[(NSInvocation*)obj setTarget:self];
	[(NSInvocation*)obj invoke];
  } else if ([obj isKindOfClass:[NSArray class]])
  {
#if 1
	NSString *type = [(NSArray*)obj objectAtIndex:0];
	if ([type isEqualToString:@"proc"]) [self execProc:obj];
	else
#endif
	  [stack addObject:obj];
	  
  }  else if ([obj isKindOfClass:[NSString class]])
  {
	NSString *identifier = obj;
	if ([identifier characterAtIndex:0]=='/')
	  [stack addObject:identifier];
	else
	  [self execName:identifier];
  } else
	[stack addObject:obj];
}

- (void) execName:(NSString *)identifier
{
  NSEnumerator *en = [dicts reverseObjectEnumerator];
  NSDictionary *d;
  id value = nil;
  while (d = [en nextObject]) {
	if (!(value = [d objectForKey:identifier])) value = [d objectForKey:[@"/" stringByAppendingString:identifier]];
	if (value) break;
  }
  
  if (value) {
	[self execObj: value];
  }
  else {
	[stack addObject:identifier];
  }
}

- (void) opDict
{
  id obj = [stack pop];
  if (![obj isKindOfClass:[NSNumber class]]) 
	[NSException raise:SimplePSInterpreterException format:@"wrong type argument for dict"];
  NSMutableDictionary *newDict = [NSMutableDictionary dictionaryWithCapacity:[(NSNumber*)obj intValue]+1];
  [newDict setObject:[NSMutableArray arrayWithCapacity:[(NSNumber*)obj intValue]] 
			  forKey:@"[KEY ORDER]"]; // KEY ORDER auxiliary array
  [stack addObject:newDict];
}


- (void) opBegin
{
  id obj = [stack pop];
  if (![obj isKindOfClass:[NSDictionary class]]) 
	[NSException raise:SimplePSInterpreterException format:@"wrong type argument for begin, expecting a dictionary"];
  [dicts addObject:obj];
}


- (void) opEnd
{
  if ([dicts count]==0)
	[NSException raise:SimplePSInterpreterException format:@"dictionary stack underflow"];
  [dicts removeLastObject];
}

- (void) opPop
{
  [stack removeLastObject];
}

- (void) opDup
{
#if 0
  id aCopy = [[stack lastObject] copy];
  [stack addObject:aCopy];
  [aCopy release];
#else
  id aCopy = [stack lastObject];
  [stack addObject:aCopy];
#endif
}

- (void) opNop
{
}

- (void) opGet
{
  id key = [stack pop];
  id target = [stack pop];
  if ([target isKindOfClass:[NSArray class]])
  {
	if (![key isKindOfClass:[NSNumber class]])
	  [NSException raise:SimplePSInterpreterException 
				  format:@"wrong type argument for get, key should be a number"];
	  
	[stack addObject:[(NSArray*)[(NSArray*)target objectAtIndex:1] objectAtIndex:[(NSNumber*)key intValue]]];	
  } else if ([target isKindOfClass:[NSDictionary class]]) {
	if (![key isKindOfClass:[NSString class]])
	  [NSException raise:SimplePSInterpreterException 
				  format:@"wrong type argument for get, key should be a number"];
	if ([(NSString*)key characterAtIndex:0] != '/')
	  [NSException raise:SimplePSInterpreterException 
				  format:@"expecting literal dictionary key for get"];	 
	id value = [(NSDictionary*)target objectForKey:key];
	if (value)
	  [stack addObject:value];
	else
	  [stack addObject:NullPSObject];
  } else
	[NSException raise:SimplePSInterpreterException 
				format:@"applying get to nonarray/dict"];  
}

- (void) opPut
{
  id value = [stack pop];
  id key = [stack pop];
  id target = [stack pop];
  if ([target isKindOfClass:[NSArray class]])
  {
	if (![key isKindOfClass:[NSNumber class]])
	  [NSException raise:SimplePSInterpreterException 
				  format:@"wrong type argument for put, key should be a number"];
	
	[(NSMutableArray*)[(NSArray*)target objectAtIndex:1] replaceObjectAtIndex:[(NSNumber*)key intValue] 
																   withObject:value];	
  } else if ([target isKindOfClass:[NSDictionary class]]) {
	if (![key isKindOfClass:[NSString class]])
	  [NSException raise:SimplePSInterpreterException 
				  format:@"wrong type argument for put, key should be a number"];
	if ([(NSString*)key characterAtIndex:0] != '/')
	  [NSException raise:SimplePSInterpreterException 
				  format:@"expecting literal dictionary key for put"];
	
	[(NSMutableDictionary*)target setObject:value forKey:key];
	[(NSMutableArray*)[(NSMutableDictionary*)target objectForKey:@"[KEY ORDER]"] 
		addObject:key];	
  } else
	[NSException raise:SimplePSInterpreterException 
				format:@"applying put to nonarray/dict"];  
  
}


- (void) opGetinterval
{
  id count = [stack pop];
  id index = [stack pop];
  id array = [stack pop];
  if ([array isKindOfClass:[NSArray class]]) {
	if ([count isKindOfClass:[NSNumber class]] && [index isKindOfClass:[NSNumber class]]) 
	  [stack addObject:[NSArray arrayWithObjects:@"array",[(NSArray*)[(NSArray*)array objectAtIndex:1] subarrayWithRange:
	   NSMakeRange([(NSNumber*)index intValue],[(NSNumber*)count intValue])],nil]];
	else
	  [NSException raise:SimplePSInterpreterException 
				  format:@"applying getinterval with nonnumber count or index"];	  
  }
  else
	[NSException raise:SimplePSInterpreterException 
				format:@"applying getinterval to nonarray"];
	
}

- (void) opPutinterval
{
  id array2 = [stack pop];
  id index = [stack pop];
  id array = [stack pop];
  if ([array isKindOfClass:[NSArray class]]) {
	if (![array2 isKindOfClass:[NSArray class]]) 
	  [NSException raise:SimplePSInterpreterException 
				  format:@"using putinterval with nonarray source"];
	if (![index isKindOfClass:[NSNumber class]]) 
	  [NSException raise:SimplePSInterpreterException 
				  format:@"using putinterval with nonnumber index"];
	NSArray *source = [(NSArray*)array2 objectAtIndex:1];
	int count = [array2 count];
	[(NSMutableArray*)[(NSArray*)array objectAtIndex:1]
	  replaceObjectsInRange:NSMakeRange([(NSNumber*)index intValue],count)
	   withObjectsFromArray:source
					  range:NSMakeRange(0,count)];
  }
  else
	[NSException raise:SimplePSInterpreterException 
				format:@"applying putinterval to nonarray"];  
}

- (void) opArray
{
  id countobj = [stack pop];
  if (![countobj isKindOfClass:[NSNumber class]]) 
	[NSException raise:SimplePSInterpreterException 
				format:@"using array with nonnumber count"];
  int count = [countobj intValue];
  NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:count];
  while(count--) [array addObject:NullPSObject];
  [stack addObject:[NSArray arrayWithObjects:@"array",array,nil]];
  [array release];
}

- (void) opMark
{
  [stack addObject:StackMarkObject];
}

- (void) opCleartomark
{
  id obj;
  while ((obj = [stack lastObject])) {
	if (obj == StackMarkObject) break;
	[stack removeLastObject];
  }
  if (obj) [stack removeLastObject];  
}

- (void) opExch
{
  id a = [stack pop];
  id b = [stack pop];
  [stack addObject:a];
  [stack addObject:b];
}

- (void) opIndex
{
  id index = [stack pop];
  if (![index isKindOfClass:[NSNumber class]]) 
	[NSException raise:SimplePSInterpreterException 
				format:@"using index with nonnumber count"];
  [stack addObject:[stack objectAtIndex:[stack count]-[(NSNumber*)index intValue]-1]];
}

- (void) opFor
{
  id proc = [stack pop];
  id limitobj = [stack pop];
  id stepobj = [stack pop];
  id startobj = [stack pop];
  if ([limitobj isKindOfClass:[NSNumber class]] && 
	  [stepobj isKindOfClass:[NSNumber class]] &&
	  [startobj isKindOfClass:[NSNumber class]] &&
	  [proc isKindOfClass:[NSArray class]] ) {
	int limit = [(NSNumber*)limitobj intValue];
	int step = [(NSNumber*)stepobj intValue];
	int start = [(NSNumber*)startobj intValue];
	for ( ;start <= limit;start += step) {
	  [stack addObject:[NSNumber numberWithInt:start]];
	  [self execProc: proc];	  
	};
  }
  else
	[NSException raise:SimplePSInterpreterException 
				format:@"wrong arguments in for"];
  
}


- (void) opKnown
{
  id key = [stack pop];
  id dict = [stack pop];
  [stack addObject:[NSNumber numberWithInt:([(NSDictionary*)dict objectForKey:key] ? 1 : 0)]];
}

- (void) opIf
{
  id trueproc = [stack pop];
  id condition = [stack pop];
  
  if (![condition isKindOfClass:[NSNumber class]])
	[NSException raise:SimplePSInterpreterException 
				format:@"condition in if is not a boolean"];
	
  if ([(NSNumber*)condition intValue]) [self execProc:trueproc];
}

- (void) opIfelse
{
  id falseproc = [stack pop];
  id trueproc = [stack pop];
  id condition = [stack pop];
  
  if (![condition isKindOfClass:[NSNumber class]])
	[NSException raise:SimplePSInterpreterException 
				format:@"condition in ifelse is not a boolean"];
  
  if ([(NSNumber*)condition intValue])
	[self execProc:trueproc];
  else  [self execProc:falseproc];
}

- (void) opCurrentdict
{
  [stack addObject:[dicts lastObject]];
}

- (void) opCurrentfile
{
  [stack addObject:@" <FILE> "];
}

- (void) opClosefile
{
  [stack pop];
}

- (void) opDefinefont
{
  id font = [stack pop];
  id key = [stack pop];
  [fontDir setObject:font forKey:key];
  [stack addObject:font];
}

NSString *decryptString(NSString *inString)
{
  unsigned const char *inbuf = [inString cString];
  unsigned long insize = [inString cStringLength];
  NSMutableData *result = [NSMutableData dataWithLength:insize];
  unsigned char *outbuf = [result mutableBytes];
  unsigned short R = 55665; /* key */
  unsigned short c1 = 52845;
  unsigned short c2 = 22719;
  unsigned long counter;
  
  for(counter = 0; counter < insize; counter++) {
	outbuf[counter] = (inbuf[counter] ^ (R>>8));
	R = (inbuf[counter] + R) * c1 + c2;
  }
//  NSString *resString = [NSString stringWithCString:outbuf length:insize];  
  NSString *resString = [[NSString alloc] initWithData:result encoding:NSMacOSRomanStringEncoding];
  return resString;
}

- (void) opEexec
{
  id fileobj = [stack pop];
  [sc scanCharactersFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]
				 intoString:nil];
  int startLoc = [sc scanLocation];
  NSString *dataString;
  if ([sc scanUpToString:@"00000000000000000000" intoString:&dataString])
  {
	NSString *decryptedStream = decryptString(dataString); 
	[inputStream deleteCharactersInRange:NSMakeRange(startLoc,[inputStream length]-startLoc)];
	[inputStream appendString:[decryptedStream substringFromIndex:4]]; // skip first 4 characters
	[sc setScanLocation:startLoc];
  } else
	[NSException raise:SimplePSInterpreterException 
				format:@"cannot find end of stream in eexec"];
}



- (void) opExec
{
  id obj = [stack pop];
  if ([obj isKindOfClass:[NSString class]]) [self execName:obj];	
  if ([obj isKindOfClass:[NSArray class]] && [(NSString*)[(NSArray*)obj objectAtIndex:0] isEqualToString:@"proc"])
  {
	[self execProc:obj];
  }
  else [NSException raise:SimplePSInterpreterException 
			  format:@"unimplemented exec feature"];
}

- (void) opCount
{
  [stack addObject:[NSNumber numberWithInt:[stack count]]];
}

- (void) opType
{
  id obj = [stack pop];
  NSString *type = @"/nulltype";
  if ([obj isKindOfClass:[NSNumber class]])
	  type = @"/integertype";
  else if ([obj isKindOfClass:[NSDictionary class]])
	type = @"/dicttype";
  else if ([obj isKindOfClass:[NSArray class]]) {
	NSString *head = [(NSArray*)obj objectAtIndex:0];
	if ([head isEqualToString:@"array"])
	  type = @"/arraytype";
	else
	  type = @"/arraytype"; // not quite correct...	  
  }   else if ([obj isKindOfClass:[NSString class]]) {
	if ([obj characterAtIndex:0] == '/') type = @"/nametype" ;
	  else
		type = @"/stringtype";
  }

  [stack addObject:type];
}

- (NSInvocation *) makeInvocationForOp:(SEL) theSelector
{
  NSMethodSignature *aSignature;
  NSInvocation *anInvocation;
  aSignature = [[self class] instanceMethodSignatureForSelector:theSelector];
  anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
  [anInvocation setSelector:theSelector];
  return anInvocation;
}

- (void)initializeInterpreter
{
  if (stack) [stack release];
  if (dicts) [dicts release];
  if (fontDir) [fontDir release];
  if (inputStream) [inputStream release];
  if (sc) [sc release];
  
  stack = [[NSMutableArray arrayWithCapacity:10000] retain];
  dicts = [[NSMutableArray arrayWithCapacity:10] retain];
  fontDir = [[NSMutableDictionary dictionaryWithCapacity:10] retain];
  inputStream = nil;
  sc = nil;
  nStoringProc = 0;
  
  NSArray *standardEncoding = [NSArray arrayWithObjects:
	@"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", 
	@"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", 
	@"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", 
	@"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", 
	@"/space", @"/exclam", @"/quotedbl", @"/numbersign", @"/dollar", @"/percent", @"/ampersand", @"/quoteright",
	@"/parenleft", @"/parenright", @"/asterisk", @"/plus", @"/comma", @"/hyphen", @"/period", @"/slash", 
	@"/zero", @"/one", @"/two", @"/three", @"/four", @"/five", @"/six", @"/seven", 
	@"/eight", @"/nine", @"/colon", @"/semicolon", @"/less", @"/equal", @"/greater", @"/question", 
	@"/at", @"/A", @"/B", @"/C", @"/D", @"/E", @"/F", @"/G",
	@"/H", @"/I", @"/J", @"/K", @"/L", @"/M", @"/N", @"/O", 
	@"/P", @"/Q", @"/R", @"/S", @"/T", @"/U", @"/V", @"/W", 
	@"/X", @"/Y", @"/Z", @"/bracketleft", @"/backslash", @"/bracketright", @"/asciicircum", @"/underscore",
	@"/quoteleft", @"/a", @"/b", @"/c", @"/d", @"/e", @"/f", @"/g", 
	@"/h", @"/i", @"/j", @"/k", @"/l", @"/m", @"/n", @"/o",
	@"/p", @"/q", @"/r", @"/s", @"/t", @"/u", @"/v", @"/w",
	@"/x", @"/y", @"/z", @"/braceleft", @"/bar", @"/braceright", @"/asciitilde", @"/.notdef",
	@"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef",
	@"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef",
	@"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", 
	@"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef",
	@"/.notdef", @"/exclamdown", @"/cent", @"/sterling", @"/fraction", @"/yen", @"/florin", @"/section", 
	@"/currency", @"/quotesingle", @"/quotedblleft", @"/guillemotleft", @"/guilsinglleft", @"/guilsinglright", @"/fi", @"/fl", 
	@"/.notdef", @"/endash", @"/dagger", @"/daggerdbl", @"/periodcentered", @"/.notdef", @"/paragraph", 
	@"/bullet", @"/quotesinglbase", @"/quotedblbase", @"/quotedblright", @"/guillemotright", @"/ellipsis", @"/perthousand", @"/.notdef", @"/questiondown", 
	@"/.notdef", @"/grave", @"/acute", @"/circumflex", @"/tilde", @"/macron", @"/breve", @"/dotaccent", 
	@"/dieresis", @"/.notdef", @"/ring", @"/cedilla", @"/.notdef", @"/hungarumlaut", @"/ogonek", @"/caron", 
	@"/emdash", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", 
	@"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", 
	@"/.notdef", @"/AE", @"/.notdef", @"/ordfeminine", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", 
	@"/Lslash", @"/Oslash", @"/OE", @"/ordmasculine", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", 
	@"/.notdef", @"/ae", @"/.notdef", @"/.notdef", @"/.notdef", @"/dotlessi", @"/.notdef", @"/.notdef", 
	@"/lslash", @"/oslash", @"/oe", @"/germandbls", @"/.notdef", @"/.notdef", @"/.notdef", @"/.notdef", nil
	];
  
  NSMutableDictionary *userdict = [[NSDictionary dictionaryWithObjectsAndKeys:
	  fontDir, @"/FontDirectory",
	  [NSArray arrayWithObjects: @"array", standardEncoding, nil], @"/StandardEncoding",
	  nil	
  ] mutableCopy];
  
  NSMutableDictionary *systemdict = [NSMutableDictionary dictionaryWithCapacity:1000];
  NSMutableDictionary *errordict = [NSMutableDictionary dictionaryWithCapacity:10];

  NSDictionary *auxDict = [NSDictionary dictionaryWithObjectsAndKeys:
	[self makeInvocationForOp:@selector(opDef)], @"def",
	[self makeInvocationForOp:@selector(opDup)], @"dup",
	[self makeInvocationForOp:@selector(opIndex)], @"index",
	[self makeInvocationForOp:@selector(opExch)], @"exch",
	[self makeInvocationForOp:@selector(opFor)], @"for",
	[self makeInvocationForOp:@selector(opDict)], @"dict",
	[self makeInvocationForOp:@selector(opArray)], @"array",
	[self makeInvocationForOp:@selector(opMark)], @"mark",
	[self makeInvocationForOp:@selector(opCleartomark)], @"cleartomark",
 	[self makeInvocationForOp:@selector(opBegin)], @"begin",
	[self makeInvocationForOp:@selector(opEnd)], @"end",
	[self makeInvocationForOp:@selector(opExec)], @"exec",
	[self makeInvocationForOp:@selector(opGet)], @"get",
	[self makeInvocationForOp:@selector(opPut)], @"put",
	[self makeInvocationForOp:@selector(opGetinterval)], @"getinterval",
	[self makeInvocationForOp:@selector(opPutinterval)], @"putinterval",
	[self makeInvocationForOp:@selector(opPop)], @"pop",
	[self makeInvocationForOp:@selector(opString)], @"string",
	[self makeInvocationForOp:@selector(opReadstring)], @"readstring",
	[self makeInvocationForOp:@selector(opNop)], @"readonly",
	[self makeInvocationForOp:@selector(opNop)], @"executeonly",
	[self makeInvocationForOp:@selector(opNop)], @"noaccess",
	[self makeInvocationForOp:@selector(opNop)], @"bind",
	[self makeInvocationForOp:@selector(opIf)], @"if",
	[self makeInvocationForOp:@selector(opIfelse)], @"ifelse",
	[self makeInvocationForOp:@selector(opKnown)], @"known",
	[self makeInvocationForOp:@selector(opNot)], @"not",
	[self makeInvocationForOp:@selector(opCurrentdict)], @"currentdict",
	[self makeInvocationForOp:@selector(opCurrentfile)], @"currentfile",
	[self makeInvocationForOp:@selector(opClosefile)], @"closefile",
	[self makeInvocationForOp:@selector(opEexec)], @"eexec",
	[self makeInvocationForOp:@selector(opDefinefont)], @"definefont",
	/* more operators */
	[self makeInvocationForOp:@selector(opNe)], @"ne",
	[self makeInvocationForOp:@selector(opEq)], @"eq",
	[self makeInvocationForOp:@selector(opCount)], @"count",
	[self makeInvocationForOp:@selector(opType)], @"type",
    /* some variables */
	[NSNumber numberWithInt:1], @"true",
	[NSNumber numberWithInt:0], @"false",
	userdict, @"userdict",
	systemdict, @"systemdict",
	errordict, @"errordict",
	nil];
	
  [errordict setObject:[NSArray arrayWithObjects:@"proc", 
	[NSArray arrayWithObjects:@"pop",nil],nil]
				forKey:@"/invalidaccess"];
  
  [systemdict addEntriesFromDictionary:auxDict];

  [dicts addObject:systemdict];
  [dicts addObject:userdict];
  
}


- (void)doString:(NSString *)anInputString
{
  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

  
  inputStream = [[anInputString mutableCopy] retain];
  
  NSString *identifierString;

  if (sc) [sc release];
  sc = [[NSScanner scannerWithString:inputStream] retain];

//  NSCharacterSet  *sCS= [NSCharacterSet whitespaceAndNewlineCharacterSet];
  NSCharacterSet  *sCS= [NSCharacterSet characterSetWithCharactersInString:@" \t\n\r"];
  NSCharacterSet  *idCS= [NSCharacterSet characterSetWithCharactersInString:@" \t\n\r[]{}()%/"];
  NSCharacterSet  *nlCS= [NSCharacterSet characterSetWithCharactersInString:@"\n\r"];
  NSCharacterSet  *parCS= [NSCharacterSet characterSetWithCharactersInString:@"()"];
  float tmpFloat;
  
  while (![sc isAtEnd]) {
	unichar ch;
	
	/* 
	   BUG? 
	   [sc scanCharactersFromSet:sCS intoString:nil]; does not scan through /r at
	   the beginning of the string. Actually it does not scan through ANY of the
	   characters in cCS!!!
	   we must do it by hand ...
	*/
	
	while (1) {
	[sc scanCharactersFromSet:sCS intoString:nil];
	ch =[inputStream characterAtIndex:[sc scanLocation]];
	if  (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t')
	  [sc setScanLocation:(int)[sc scanLocation]+1];
	  else break;
	};
	switch (ch)
	{
	  case '%'  : // comment
			 [sc scanUpToCharactersFromSet:nlCS intoString:nil];
			[sc setScanLocation:[sc scanLocation]+1];
			 continue;
	  case '/' : // literal
		    [sc scanString:@"/" intoString:nil];
			if ([sc scanUpToCharactersFromSet:idCS intoString:&identifierString]) {
			  [stack addObject:[@"/" stringByAppendingString:identifierString]];
			}
			continue;
	  case '('  : // string
			{
			  int nesting = 0;
			  NSMutableString *curString = [NSMutableString stringWithCapacity:100];
			  NSString *fragment;
		      [sc scanString:@"(" intoString:nil];
		      while (1) {
				[sc scanUpToCharactersFromSet:parCS intoString:&fragment];
				[curString appendString:fragment];
				if ([inputStream characterAtIndex:[sc scanLocation]] == '(')
				{
				  nesting++; [curString appendString:@"("]; [sc scanString:@"(" intoString:nil];
				}
				else {
				  [sc scanString:@")" intoString:nil];
				  if (nesting) {
					nesting--; [curString appendString:@")"]; 
				  }
				  else break;
				}
			  }
			  [stack addObject:curString];
			}	  
			continue;
	  case '{'  : // begin proc
			[sc scanString:@"{" intoString:nil];
		    nStoringProc++;
			[stack addObject:StackMarkObject];
		    continue;
	  case '}'  : // end proc
			{
		    [sc scanString:@"}" intoString:nil];
		    nStoringProc--;
			id obj;
			NSMutableArray *proc = [NSMutableArray arrayWithCapacity:100];
			while ((obj = [stack lastObject]) != StackMarkObject) {
			  [proc insertObject:obj atIndex:0];
			  [stack removeLastObject];
			}
			if (obj == StackMarkObject) [stack removeLastObject];
			[stack addObject:[NSArray arrayWithObjects:@"proc",proc,nil]];
			}
		    continue;
	  case '['  : // begin array
		    [sc scanString:@"[" intoString:nil];
			[stack addObject:StackMarkObject];
			continue;
	  case ']'  : // end array
	        {
		    [sc scanString:@"]" intoString:nil];
			id obj;
			NSMutableArray *array = [NSMutableArray arrayWithCapacity:100];
			while ((obj = [stack lastObject]) != StackMarkObject) {
			  [array insertObject:obj atIndex:0];
			  [stack removeLastObject];
			}
			if (obj == StackMarkObject) [stack removeLastObject];
			[stack addObject:[NSArray arrayWithObjects:@"array",array,nil]];
		    }
		    continue;

	  case '<' : // hex string		  
		[NSException raise:SimplePSInterpreterException format:@"hexstring not yet implemented!"];
		continue;
	
	default : // check if it is a number or a symbol
	  
	  if ([sc scanFloat:&tmpFloat]) {
		[stack addObject:[NSNumber numberWithFloat:tmpFloat]];
	  } else if ([sc scanUpToCharactersFromSet:idCS intoString:&identifierString]) {
		if (nStoringProc) {
		  [stack addObject:identifierString];
		} else {
		  [self execName:identifierString];
		}
	  }	else
		[NSException raise:SimplePSInterpreterException format:@"Exhausted possible cases. I'm lost!"];
	}
  }

  [sc release];
  [inputStream release];
  sc = nil;
  inputStream = nil;
  
  [pool release];
}


- init
{
  self = [super init];
  if (self) {
	[self initializeInterpreter];
  }
  return self;
}

- (void) dealloc
{
  [stack release];
  [fontDir release];
  [dicts release];
  
  [super dealloc];
}


- (NSDictionary*)fontDir
{
  return fontDir;
}

@end
